﻿using System;
using System.IO;
using Aeon.Framework;
using System.Windows.Forms;
using Aeon.Framework.Objects;
using Aeon.Testing.Properties;
using Aeon.Framework.Objects.Models;

namespace Aeon.Testing.Animations
{

public partial class WindowDemoAni : Form
    {
    // --- Attributes:
    
    private Boolean back;           // Play direction.
    private IPlay motion;           // Animation object.
    private String selected;        // Current animation name.

    private World scene;            // Scene graphics handler.    
    private Timer timer;            // Frame timer controller.    
    private Boolean playing;        // Play button flag.
    private Position memory;        // Screen clicks.
    
    //private static String skyT = path+"sky.jpg";
    //private static String grassT = path+"grass.jpg";
    private static String path = Application.StartupPath+@"\Demos\Content\";
    
    // --- Constructor:

    public WindowDemoAni()
        {
        InitializeComponent();
        Icon = Resources.AeonIco;
        
        // Prepare selection box:
        
        string[] files = Directory.GetFiles(path);
        foreach(string s in files) { if (s.Contains(".bvh")) BoxSelection.Items.Add(Path.GetFileName(s)); }
        
        BoxSelection.SetSelected(0,true);

        // Set initial window state:
        
        motion = null;
        buttonStates(false);
        
        // Prepare GL:
        
        Display.InitializeContexts();
        
        startWorld();        
        
        Display.MouseUp += PanHandler;
        Display.Resize += SizeHandler;
        Display.Paint += RenderHandler;        
        Display.MouseDown += ClickHandler;
        Display.MouseWheel += ZoomHandler;
        Display.PreviewKeyDown += KeyHandler;
        }

    // --- Internal Methods:
    
    /// <summary>
    /// Initializes and prepares world.
    /// 
    /// </summary>
    private void startWorld()
        {
        // Create world:
        
        WorldManager.SetModules(typeof(AeonTextureManager),typeof(AeonFontManager),typeof(MockAudioManager),typeof(MockInputManager),typeof(MockPhysicsManager));
        AeonColors.Default = AeonColors.Black;
        scene = WorldManager.CreateWorld("Test World");
        scene.SetCamera(Display.Height,Display.Width);
        scene.CameraMoveTo(new Position(0,0,500));
        scene.CameraTurnTo(new Framework.Orientation(-40,0,0));
        
        // Set timer:
        
        timer = new Timer();
        timer.Interval = 55;
        timer.Tick += Timer_Tick;
        
        timer.Start();
        
        // Set scene:
        
        AeonColor ambU = new AeonColor(0.2f,0.2f,0.2f,1.0f);
        AeonColor difU = new AeonColor(0.8f,0.8f,0.8f,1.0f);
        AeonColor spcU = new AeonColor(0.5f,0.5f,0.5f,1.0f);
        Position posU = new Position(0.0f,100.0f,0.0f);

        AeonLight light = new AeonLight("MainLight",posU,ambU,difU,spcU);
        scene.Add(light);
        }
    
    /// <summary>
    /// Ends world.
    /// 
    /// </summary>
    private void endWorld()
        {
        timer.Stop();
        scene.DestroyScene();
        Display.DestroyContexts();
        
        AeonColors.Default = AeonColors.White;
        }
    
    /// <summary>
    /// Starts player with selected animation.
    /// 
    /// </summary>    
    private void startPlayer()
        {
        // Set title label:
        
        String file = path+selected;
        String title = file.Substring(file.LastIndexOf(@"\")+1);
        LabelName.Text = title.Replace(".bvh"," ").Trim();
                
        // Create animation:

        motion = new Animation<ParserBVH>(selected,new Position(-90,0,90),Framework.Orientation.Origin,AeonMaterials.Default,file);
        motion.Loop = true;
        BoxLog.Text = motion.ToString(); 
        scene.Add(motion);
        
        // Ready animation:
        
        back = false;
        playing = false;
        buttonStates(true);
        }
        
    private void stopPlayer()
        {
        scene.Remove(motion.ID);
        
        motion = null;
        playing = false;
        buttonStates(false);
        }

    /// <summary>
    /// Mantains possible action states.
    /// 
    /// </summary>
    private void buttonStates(bool status)
        {
        ButtonFinish.Enabled = status;
        ButtonNext.Enabled = status;
        ButtonPlay.Enabled = status;
        ButtonPrevious.Enabled = status;
        ButtonRewind.Enabled = status;
        
        if (status)
           {
           if (playing)
              {
              ButtonPlay.Text = ";";
              toolTips.SetToolTip(ButtonPlay,"Pause");
              }

           else {
                ButtonPlay.Text = "4";
                toolTips.SetToolTip(ButtonPlay,"Play");
                }
           }
        }

    /// <summary>
    /// Play continuously in the set direction..
    /// 
    /// </summary>
    private void play()
        {
        if (back) motion.AnimateBackward();
        else motion.AnimateForward();
        
        playing = true;
        buttonStates(true);
        }

    /// <summary>
    /// Pauses on current frame.
    /// 
    /// </summary>
    private void pause()
        {
        playing = false;
        buttonStates(true);
        }

    /// <summary>
    /// Resets to first frame.
    /// 
    /// </summary>
    private void rewind()
        {
        motion.AnimateStop();
        
        playing = false;
        buttonStates(true);
        }

    /// <summary>
    /// Shows next frame.
    /// 
    /// </summary>
    private void next()
        {
        playing = false;
        back = false;
        
        motion.AnimateForward();
        
        buttonStates(true);
        }

    /// <summary>
    /// Show previous frame.
    /// 
    /// </summary>
    private void prev()
        {
        playing = false;
        back = true;
        
        motion.AnimateBackward();
        
        buttonStates(true);
        }

    /// <summary>
    /// Jump to the last frame.
    /// 
    /// </summary>
    private void finish()
        {
        playing = false;
        
        motion.AnimateEnd();
                
        buttonStates(true);
        }

    // --- Component Event Handlers:
    
    /// <summary>
    /// Free resources and really close window.
    /// Also captures the red X button.
    /// 
    /// </summary>
    private void WindowDemoAni_FormClosing(object sender,FormClosingEventArgs e)
        {
        
        endWorld();
        }
    
    /// <summary>
    /// Free resources and close window.
    /// 
    /// </summary>
    private void ButtonExit_Click(object sender,EventArgs e)
        {
        
        this.Close();
        }
    
    /// <summary>
    /// Change selected filename.
    /// 
    /// </summary>
    private void BoxSelection_SelectedValueChanged(object sender, EventArgs e)
        {

        selected = BoxSelection.SelectedItem as String;
        }
        
    /// <summary>
    /// Select animation and ready player.
    /// 
    /// </summary>
    private void ButtonSelect_Click(object sender, EventArgs e)
        {
        if (motion!=null) stopPlayer();
        
        startPlayer();        
        }

    /// <summary>
    /// Handler for button Click.
    /// Plays the animation.
    /// 
    /// </summary>
    private void ButtonPlay_Click(object sender, EventArgs e)
        {
        if (!playing) { play(); }
        else { pause(); }
        }

    /// <summary>
    /// Handler for button Click.
    /// Jumps to first frame.
    /// 
    /// </summary>
    private void ButtonRewind_Click(object sender, EventArgs e)
        {

        rewind();
        }

    /// <summary>
    /// Handler for button Click.
    /// Jumps to previous frame.
    /// 
    /// </summary>
    private void ButtonPrevious_Click(object sender, EventArgs e)
        {

        prev();
        }

    /// <summary>
    /// Handler for button Click.
    /// Jumps to next frame.
    /// 
    /// </summary>
    private void ButtonNext_Click(object sender, EventArgs e)
        {

        next();
        }

    /// <summary>
    /// Handler for button Click.
    /// Jumps to last frame.
    /// 
    /// </summary>
    private void ButtonFinish_Click(object sender, EventArgs e)
        {

        finish();
        }

    /// <summary>
    /// Executes at every tick of the timer.
    /// Updates scene information and redraws.
    /// 
    /// </summary>
    private void Timer_Tick(Object sender, EventArgs e)
        {
        if (!playing && motion!=null) motion.AnimatePause();
        
        Display.Invalidate();
        }

    // --- OpenGL Component Event Handlers:

    /// <summary>
    /// Handler for the OnPaint event.
    /// 
    /// </summary>    
    public void RenderHandler(Object obj, PaintEventArgs arg)
        {
        
        scene.RenderScene();
        }

    /// <summary>
    /// Handler for Resize events.
    /// 
    /// </summary>  
    public void SizeHandler(Object obj, EventArgs arg)
        {
        
        scene.SetCamera(Display.Height,Display.Width);
        }

    /// <summary>
    /// Handler for the MouseWheel event.
    /// Mouse wheel controls zoom.
    /// 
    /// </summary>
    public void ZoomHandler(Object obj, MouseEventArgs arg)
        {

        scene.CameraMoveBy(new VectorC(0,0,arg.Delta/1.3f));
        }

    /// <summary>
    /// Handler for MouseDown event.
    /// Click sets position memory for the PanHandler.
    /// 
    /// </summary>    
    public void ClickHandler(Object obj, MouseEventArgs arg)
        {
        switch(arg.Button)
            {
            case MouseButtons.Right: memory.Change(new Position(arg.X,arg.Y)); break;
            case MouseButtons.Middle: memory.Change(new Position(arg.X,arg.Y)); break;
            }
        }

    /// <summary>
    /// Handler for MouseUp event.
    /// Button drag delta controls translation and rotation pan.
    /// 
    /// </summary>    
    public void PanHandler(Object obj, MouseEventArgs arg)
        {
        int posPrecision = 3;
        int rotPrecision = 5;

        switch(arg.Button)
            {
            case MouseButtons.Right:
                    {    
                    float x = -((arg.X-Display.Height/2)-(memory.xPos-Display.Height/2))/posPrecision;
                    float y = ((arg.Y-Display.Width/2)-(memory.yPos-Display.Width/2))/posPrecision;

                    scene.CameraMoveBy(new VectorC(x,y,0));
                    break;              
                    }

            case MouseButtons.Middle:
                {
                float x = -((arg.Y-Display.Height/2)-(memory.yPos-Display.Height/2))/rotPrecision;
                float z = -((arg.X-Display.Width/2)-(memory.xPos-Display.Width/2))/rotPrecision;

                scene.CameraTurnBy(new Framework.Orientation(x,0,z));
                break;
                }
            }
        }

    /// <summary>
    /// Handler for KeyPress event.
    /// Key redundancy for mouse and button actions.
    /// 
    /// [TODO] InputHandler.
    /// 
    /// </summary>
    private void KeyHandler(Object sender, PreviewKeyDownEventArgs arg)
        {
        switch(arg.KeyCode)
            {                                                           // Animation Control:
            case Keys.B: prev(); break;                                 // Backward
            case Keys.F: next(); break;                                 // Forward
            case Keys.R: rewind(); break;                               // Rewind
            case Keys.E: finish(); break;                               // End            
            case Keys.P: if (!playing) play(); else pause(); break;     // Play/Pause

                                                                                            // Camera Control:
            case Keys.PageUp: scene.CameraMoveBy(new VectorC(0,0,-10));             break;  // Zoom more
            case Keys.PageDown: scene.CameraMoveBy(new VectorC(0,0,10));            break;  // Zoom less
            case Keys.Up: scene.CameraTurnBy(new Framework.Orientation(-15,0,0));   break;  // Rotate up
            case Keys.Down: scene.CameraTurnBy(new Framework.Orientation(15,0,0));  break;  // Rotate down
            case Keys.Left: scene.CameraTurnBy(new Framework.Orientation(0,-15,0)); break;  // Rotate left
            case Keys.Right: scene.CameraTurnBy(new Framework.Orientation(0,15,0)); break;  // Rotate right
            case Keys.NumPad2: scene.CameraMoveBy(new VectorC(0,-20,0));            break;  // Move down
            case Keys.NumPad8: scene.CameraMoveBy(new VectorC(0,20,0));             break;  // Move up
            case Keys.NumPad4: scene.CameraMoveBy(new VectorC(-20,0,0));            break;  // Move left
            case Keys.NumPad6: scene.CameraMoveBy(new VectorC(20,0,0));             break;  // Move right
            case Keys.NumPad5: scene.CameraMoveTo(new Position(0,0,200));                   // Center camera
                               scene.CameraTurnTo(new Framework.Orientation(-20,0,0));
                               break;
            }

        // Prevents arrows from losing focus
        arg.IsInputKey = true;
        }

    }


}
